home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / Prog / U-Z / VideoToolBox Folder / Demos / TimeVideo.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-31  |  21.9 KB  |  528 lines  |  [TEXT/KAHL]

  1. /*
  2. TimeVideo.c
  3. Copyright © Denis G. Pelli, 1992, 1993
  4.  
  5. TimeVideo does a thorough test of the timing and synchronization of each video
  6. screen, as well as the integrity of the clut hardware and software, and saves
  7. the results in self-explanatory text file, “TimeVideo results”. A discussion
  8. of movies, lookup table animation, and synchronization appears in the
  9. VideoToolbox “Video synch” file. The many video driver bugs uncovered by
  10. TimeVideo and its predecessors are reported in that file. (For example, the
  11. TrueVision NuVista driver assumes zero start in 16- and 32-bit modes.) Please
  12. add your results by emailing your “TimeVideo report” to denis_pelli@isr.syr.edu.
  13.  
  14. Despite Apple's rules that require it, a few video drivers don't allow you to
  15. read the clut (i.e. use GDGetEntries), or crash if you attempt it. GDGetEntries
  16. (in GDVideo.c) checks against a list of known offenders and returns a statusErr
  17. is the call is not usable. If we can't use GDGetEntries then we fall back to a
  18. visual test of the clut, comparing a particular way of loading the clut
  19. against the standard way of loading the clut. Any visible change between these 
  20. two conditions suggests an error in the non-standard way of loading the clut.
  21.  
  22. BUGS:
  23. Version 1.0 hung up on a PowerBook 170 with an Envisio video adapter. I didn't 
  24. have a chance to figure out why. It works fine on my ordinary PowerBook 170.
  25.  
  26. HISTORY:
  27. 8/23/92    dgp    wrote it, based on my TimeCPU.c
  28. 8/26/92    dgp print the driver version only if it's nonzero.
  29.             added summary at end of printout.
  30. 9/9/92    dgp    changed printout of system vbl rate from %.1f to %.2f
  31.             Added movie rate to measurements and printout.
  32. 9/15/92    dgp    made compatible with System 6.04. Added header to results file.
  33. 9/17/92    dgp    Added QUICKLY switch, to select CopyBits or CopyBitsQuickly.
  34. 10/5/92    dgp    cosmetic changes
  35. 10/6/92    dgp    report program version. Use frames/clut update as the criterion for summary.
  36. 10/9/92    dgp    Fixed summary. Too slow means MORE than one frame per clut update.
  37.             Fixed restoration of clut in direct modes (i.e. 16 and 32 bit modes).
  38.             Renamed to GDRestoreDeviceClut and put it in GDVideo.c.
  39. 10/13/92 dgp Tom Busey reported that frames were going uncounted
  40.             during the clut timing, which seems to be a problem with some video drivers.
  41.             TimeVideo now reports a frame count based solely on timing in secs and
  42.             the separately measured frame rate, which seems to be a reliable.
  43.             It also double checks the timing in secs vs frames, and if
  44.             it finds a discrepancy, prints a warning to the screen & file. 
  45. 10/20/92 dgp Added VBL/frame to summary.
  46. 12/8/92    dgp Minor editing of comments.
  47. 12/9/92    dgp    1.01 Added _atexit(RestoreCluts) in case user quits prematurely.
  48. 12/11/92 dgp 1.02 GDRestoreBlackAndWhite allocates stack space for linearTable
  49.             only if we need it.
  50. 12/30/92 dgp 1.03 cosmetic changes.
  51. 12/30/92 dgp 1.04 Enhanced summary to account for NAN when no clut access is allowed.
  52. 12/30/92 dgp 1.05 Use GDClutSize().
  53. 1/6/93    dgp     1.06 Simplified GDRestoreBlackAndWhite, eliminated linearTable.
  54. 1/7/93    dgp  1.07 Included in VideoToolbox-1-93.sea
  55. 1/8/93    dgp     1.08 Added Info-Mac to report file.
  56. 1/11/93    dgp     1.09 In response to bug report from jonathan brecher, added support
  57.             for computers that lack Color QuickDraw.
  58. 1/15/93    dgp    1.10 Added version resource.
  59. 1/18/93    dgp    1.11 Updated the explanatory text.
  60. 1/24/93 dgp    1.12 Updated the explanatory text.
  61. 2/6/93 dgp    1.13 Report ROM version.
  62. 2/15/93 dgp    1.14 Report ROM version as 124+6*256.
  63. 2/22/93    dgp    1.15 Recompiled.
  64. 2/27/93    dgp    1.16 Recompiled with new Identify.c.
  65. 3/10/93    dgp    1.17 Choose a valid mode for which to print the frame rate.
  66.                  Moved all the timing code into GDInfo.c, so this routine just prints.
  67. 3/12/93    dgp        Reorganized the printout and added TestCluts's capabilities.
  68. 3/13/93    dgp        Added high-priority timing.
  69. 3/16/93    dgp    2.0 Added visual hash inspection.
  70. 3/31/93    dgp    2.1 Release version, clut test seems to work.
  71. */
  72. #include "VideoToolbox.h"
  73. #include <Packages.h>
  74. #include <Traps.h>
  75. #include <math.h>
  76. #include "GDInfo.h"
  77. void TimeVideo(void);
  78. void PrintCard(FILE *o[2],GDHandle device,VideoCard *card);
  79. void GDRestoreBlackAndWhite(GDHandle device);
  80. #define VERSION "2.1"
  81.  
  82. void main(void)
  83. {
  84.     Require(gestaltOriginalQD);
  85.     TimeVideo();
  86. }
  87.  
  88. void TimeVideo(void)
  89. {
  90.     long system;
  91.     char string[1000],datafilename[]="TimeVideo results";
  92.     FILE *o[2],*dataFile;
  93.     int newDataFile;
  94.     unsigned long time;
  95.     Str255 todayStr;
  96.     GDHandle device;
  97.     short i,m,error,oldPixelSize,mode,cards,modes;
  98.     double frames,s,vblPerFrame,f;
  99.     Rect r;
  100.     static VideoCard card[MAX_SCREENS];    // "static" will initialize to zero.
  101.     VideoCardClutTest *clut;
  102.     short visualTest,hashTest;
  103.     
  104.     StackGrow(10000);
  105.     MaximizeConsoleHeight();
  106.     #if THINK_C
  107.         console_options.title="\pTimeVideo";
  108.     #endif
  109.     printf("\n");    // ask THINK C to initialize quickdraw
  110.     
  111.     sprintf(string,"Welcome to TimeVideo " VERSION "\n\n"
  112.     "This program will thoroughly test all your video devices and save the "
  113.     "results in the text file “%s”. Don’t be alarmed by the strange "
  114.     "antics of your screens. Everything will soon be back to normal. Just sit back "
  115.     "and enjoy the show.",datafilename);
  116.     sprintf(string,"%s You may quit at any time by hitting Command-period.",string);
  117.     printf(BreakLines(string,80));
  118.     printf("\n\nWould you like the testing to include a visual inspection?");
  119.     visualTest=YesOrNo(1);
  120.     printf("\n");
  121.     printf(BreakLines("\n"
  122.         "TimeVideo times everything: the video frames, interrupts, and cluts. And it "
  123.         "determines what fraction of the screen you can fill with a real-time movie shown "
  124.         "by CopyBits() or CopyBitsQuickly(). Then it does write-then-read tests of the "
  125.         "clut, to make sure the hardware and software, GDSetEntries or SetEntriesQuickly, "
  126.         "are working correctly.\n\n",80));
  127.     dataFile=fopen(datafilename,"r");
  128.     if(dataFile!=NULL)fclose(dataFile);
  129.     newDataFile=(dataFile==NULL);
  130.     o[0]=stdout;
  131.     o[1]=dataFile=fopen(datafilename,"a");    /* Append to data file */
  132.     if(dataFile!=NULL)SetFileInfo(datafilename,'TEXT','ttxt');
  133.     else printf("Could neither open nor create \"%s\". Perhaps your disk is locked.\n",datafilename);
  134.     if(newDataFile){
  135.         fprintf(dataFile,BreakLines(
  136.         "This data file  reports the timing and accuracy of all your video screens, "
  137.         "as measured by TimeVideo, a component of the VideoToolbox.\n"
  138.         "\nTHE VIDEO TOOLBOX\n"
  139.         "The VideoToolbox is a "
  140.         "collection of nearly two hundred C subroutines and several demo and utility "
  141.         "programs written to do visual psychophysics with Macintosh computers. It's free "
  142.         "and may not be sold without permission. It should be useful to anyone who wants "
  143.         "to present accurately specified visual stimuli or use the Mac for psychometric "
  144.         "experiments. The text file “Video synch” discusses all the ways of synchronizing "
  145.         "programs to video displays and the many pitfalls to avoid. The TimeVideo "
  146.         "application checks out the timing of all video devices in anticipation of their "
  147.         "use in critical real-time applications, e.g. movies or lookup-table animation. "
  148.         "Low-level routines control video timing and lookup tables, display real-time "
  149.         "movies, and implement the luminance-control algorithms suggested by Pelli and "
  150.         "Zhang. (D.G. Pelli and L. Zhang, 1991, Accurate control of contrast on "
  151.         "microcomputer displays. Vision Research, 31, 1337-1350. Reprints are available.) "
  152.         "High-level routines help analyze psychophysical experiments (e.g. graphing or "
  153.         "maximum-likelihood fitting of psychometric data). This collection has been "
  154.         "continually updated since 1991. Many colleagues have indicated that they are "
  155.         "using the software in their labs. Most of the routines are Mac-specific, but "
  156.         "some very useful routines, e.g. the luminance-control, statistics, and "
  157.         "maximum-likelihood fitting algorithms, could easily be ported to other "
  158.         "computers. To get the latest version of the VideoToolbox, just send me your "
  159.         "mailing address, and I’ll mail you a disk. Or download “VideoToolbox.sea” (a "
  160.         "self-extracting archive) electronically from the Info-Mac or MacPsych archives. "
  161.         "The ftp servers are called sumex-aim.stanford.edu [36.44.0.6] (look in "
  162.         "info-mac/source/c) and ftp.stolaf.edu [130.71.128.9] (look in the pub/macpsych "
  163.         "directory). Log in as “anonymous”; any password will do.\n",80));
  164.     fprintf(dataFile,
  165.         "Denis Pelli, Professor of Neuroscience\n"
  166.         "Institute for Sensory Research, Syracuse University, Syracuse, NY 13244-5290, USA\n"
  167.         "denis_pelli@isr.syr.edu\n");
  168.     fprintf(dataFile,BreakLines(
  169.         "\nTIME VIDEO\n"
  170.         "For each video card, TimeVideo measures the video frame rate, frequency of VBL "
  171.         "interrupts (supposed to be one per frame), how long it takes to load the clut, "
  172.         "and how much of the screen you can fill with a real-time one-image-per-frame "
  173.         "movie shown by CopyBits() or CopyBitsQuickly(). It then performs a random "
  174.         "write-then-read test of the Color Lookup Table (clut). This tests the clut "
  175.         "memory hardware and the software used to write and read the clut. We test "
  176.         "writing by GDSetEntries(), which passes the request on to the video driver, and "
  177.         "we also test writing by SetEntriesQuickly(), which accesses the hardware "
  178.         "directly. In either case, the clut is read by GDGetEntries(), which passes the "
  179.         "request on to the video driver. The testing is thorough; many video devices fail "
  180.         "at least part of the test. All the driver errors uncovered to date appear in the "
  181.         "VideoToolbox “Video synch” text file. Add your results by emailing this file to "
  182.         "denis_pelli@isr.syr.edu\n"
  183.         "\nGLOSSARY\n"
  184.         "A video frame is a refresh of your video screen. To show a movie, you will want "
  185.         "to reload the image once per frame; the table shows how big that image can be, "
  186.         "as a fraction of the screen area, and still be reloaded once per frame. "
  187.         "(Some video cards have multiple video “pages” that can be switched by "
  188.         "calling GDSetMode(), though I've never tried it.) A “VBL” "
  189.         "interrupt is produced by your video card driver, nominally once per frame, but "
  190.         "some video drivers produce more, which is poor, but not serious. (The "
  191.         "VBLInstall.c program will deal with it.) Suppressed interrupts during clut "
  192.         "updates are bad. It means that the driver disables the VBL interrupt for too "
  193.         "long while it’s loading the clut. This will throw off any interrupt-based "
  194.         "attempt to count frames. The clut is the color lookup table of your video card. "
  195.         "Lookup table animation, e.g. for temporal modulation of contrast, requires that "
  196.         "you reload the clut once per frame, so it’s very important that this be fast "
  197.         "enough. "
  198.         "The first call to SetEntriesQuickly() for each device is slow--a cache is "
  199.         "filled; the reported times are for subsequent calls. "
  200.         "Each video card can be in “Color” or “Gray” mode, as set by the Control "
  201.         "Panel:Monitors, the Macintosh Toolbox call SetDepth, or the VideoToolbox call "
  202.         "GDSetGray. In “Gray” mode all colors are transformed to luminance-equivalent "
  203.         "grays. The clut tests try loading the clut serially, one entry at a time, and "
  204.         "all at once; some video drivers fail the serial test because they don’t cope "
  205.         "well with a nonzero “start” value for the entry index. For more explanation see "
  206.         "the text file called “Video synch” on the VideoToolbox disk.\n"
  207.         "\n"
  208.         "“ok”= the clut passed all tests.\n"
  209.         "“!gray”= the clut was nominally in gray mode, but only passed the color test.\n"
  210.         "“!color”= the clut was nominally in color mode, but only passed the gray test.\n"
  211.         "“!serial”= clut passed when loaded all at once, but failed when loaded serially.\n"
  212.         "“bad”= the clut produced some other pattern of errors, reported explicitly.\n"
  213.         "SetEntriesQuickly: “!gray” is ok, and “!serial” is ok on the Quadra.\n"
  214.         "\nThis is a TeachText file, "
  215.         "but you may prefer to open it from THINK C, or a word processor, because the columns "
  216.         "in the tables will only align properly if displayed in a monospaced font.\n\n",80));
  217.     }
  218.     ffprintf(o,"TimeVideo version " VERSION "\n");
  219.     GetDateTime(&time);
  220.     IUDateString(time,longDate,todayStr);
  221.     ffprintf(o,"%#s.\n",todayStr);
  222.  
  223.     // Timer.c requires the Revised Time Manager, which appeared in System 6.0.3.
  224.     // SetDepth() is part of the "new" Palette Manager, which appeared in System 6.0.5.
  225.     Gestalt(gestaltSystemVersion,&system);
  226.     if(system<0x605) {
  227.         PrintfExit("Sorry. Your System is too old; I need at least System 6.0.5.\n");
  228.     }
  229.  
  230.     ffprintf(o,"%s\n",BreakLines(IdentifyMachine(),80));
  231.     ffprintf(o,"Tick rate is %.1f Hz.",TickRate());
  232.     ffprintf(o," System-based VBL rate is %.1f Hz.\n",GDFrameRate(NULL));
  233.     if(QD8Exists())_atexit(RestoreCluts);// In case user quits prematurely.
  234.     for(i=0;;i++){
  235.         if(QD8Exists()){
  236.             device=GetScreenDevice(i);
  237.             if(device==NULL)break;
  238.             oldPixelSize=(**(**device).gdPMap).pixelSize;
  239.         }else device==NULL;
  240.         ffprintf(o,"\n%s\n",BreakLines(IdentifyVideo(device),80));
  241.         printf("Getting card info. . .                                     \r");
  242.         error=GDInfo(device,&card[i]);
  243.         card[i].visual=hashTest=visualTest;
  244.         for(m=0;m<6;m++){
  245.             card[i].depth[m].pixelSize=0;
  246.             if(QD8Exists()){
  247.                 if(!HasDepth(device,1<<m,0,0))continue;
  248.                 error=SetDepth(device,1<<m,0,0);
  249.                 card[i].mode=(**device).gdMode;
  250.                 if(error)continue;
  251.             }else if(m>0)continue;
  252.             card[i].m=m;
  253.             card[i].depth[m].pixelSize=1<<m;
  254.             error=GDTime(device,&card[i]);
  255.             printf("%d-bit pixels: testing clut: GDSetEntries . . .                  \r"
  256.                 ,card[i].depth[m].pixelSize);
  257.             card[i].depth[m].clut.hashTest=hashTest;
  258.             card[i].depth[m].clutQuickly.hashTest=hashTest;
  259.             hashTest=0;    // Only do the hash test at one depth
  260.             card[i].depth[m].clut.tests=0;
  261.             card[i].depth[m].clutQuickly.tests=0;
  262.             error=TestClut(o,device,0,&card[i]);
  263.             error=TestClut(o,device,testClutSeriallyFlag,&card[i]);
  264.             error=TestClutHash(device,GDSetEntriesByType,&card[i].depth[m].clut);
  265.             printf("%d-bit pixels: testing clut: SetEntriesQuickly . . .             \r",card[i].depth[m].pixelSize);
  266.             error=TestClut(o,device,testClutQuicklyFlag,&card[i]);
  267.             error=TestClut(o,device,testClutQuicklyFlag|testClutSeriallyFlag,&card[i]);
  268.             error=TestClutHash(device,SetEntriesQuickly,&card[i].depth[m].clutQuickly);
  269.             printf("                                                                 \r");
  270.         }
  271.         if(QD8Exists())error=SetDepth(device,oldPixelSize,0,0);
  272.         ffprintf(o,"%dx%d pixels. ",card[i].width,card[i].height);
  273.         if(card->isGray)ffprintf(o,"Gray mode.\n");
  274.         else ffprintf(o,"Color mode.\n");
  275.         PrintCard(o,device,&card[i]);
  276.         if(!QD8Exists())break;
  277.     }
  278.     cards=i;
  279.     if(GDVersion(device)==100
  280.         && EqualString("\p.Display_Video_Apple_RBV1",GDName(device),1,1)){
  281.             ffprintf(o,
  282.             "NOTE: the built-in driver in the Mac IIci (.Display_Video_Apple_RBV1 version 0)\n"
  283.             "has a bug that causes it to crash if you attempt to read the clut. A temporary\n"
  284.             "patch has been applied that has fixed the driver until the next reboot, as\n"
  285.             "explained in the VideoToolbox file “Video synch”.\n");
  286.     }
  287.     fprintf(o[1],"\n\n");
  288.     if(dataFile!=NULL){
  289.         fclose(dataFile);
  290.         sprintf(string,"\nThe text file “%s” explains all the results.\n",datafilename);
  291.         printf(BreakLines(string,80));
  292.     }
  293. }
  294.  
  295. void PrintCard(FILE *o[2],GDHandle device,VideoCard *card)
  296. {
  297.     short i,m=card->m,hashTest,visualTest;
  298.     char string[100];
  299.     static char s1[]="%-33s",s2[]="%6s",s3[]="  %-9s\n";
  300.     VideoCardClutTest *clut;
  301.     
  302.     ffprintf(o,s1,"pixel size");
  303.     for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  304.         sprintf(string,"%d   ",card->depth[m].pixelSize);
  305.         ffprintf(o,s2,string);
  306.     }
  307.     ffprintf(o,s3,"bits");
  308.  
  309.     ffprintf(o,s1,"pages");
  310.     for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  311.         sprintf(string,"%d   ",card->depth[m].pages);
  312.         ffprintf(o,s2,string);
  313.     }
  314.     ffprintf(o,s3,"");
  315.  
  316.     ffprintf(o,s1,"frame rate");
  317.     for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  318.         sprintf(string,"%.1f ",card->depth[m].frameRate);
  319.         ffprintf(o,s2,string);
  320.     }
  321.     ffprintf(o,s3,"Hz");
  322.  
  323.     ffprintf(o,s1,"interrupts per frame");
  324.     for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  325.         sprintf(string,"%.1f ",card->depth[m].vblPerFrame);
  326.         ffprintf(o,s2,string);
  327.     }
  328.     ffprintf(o,s3,"");
  329.  
  330.     ffprintf(o,s1,"CopyBits movie size");
  331.     for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  332.         sprintf(string,"%.2f",card->depth[m].movieRate/card->depth[m].frameRate);
  333.         ffprintf(o,s2,string);
  334.     }
  335.     ffprintf(o,s3,"screen");
  336.  
  337.     ffprintf(o,s1,"CopyBitsQuickly movie size");
  338.     for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  339.         sprintf(string,"%.2f",card->depth[m].movieRateQuickly/card->depth[m].frameRate);
  340.         ffprintf(o,s2,string);
  341.     }
  342.     ffprintf(o,s3,"screen");
  343.  
  344.     ffprintf(o,s1,"CopyBits data rate");
  345.     for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  346.         sprintf(string,"%.2f",card->depth[m].movieRate
  347.             *card->depth[m].pixelSize
  348.             *card->width*card->height/8./1024./1024.);
  349.         ffprintf(o,s2,string);
  350.     }
  351.     ffprintf(o,s3,"MB/s");
  352.  
  353.     ffprintf(o,s1,"CopyBitsQuickly data rate");
  354.     for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  355.         sprintf(string,"%.2f",card->depth[m].movieRateQuickly
  356.             *card->depth[m].pixelSize
  357.             *card->width*card->height/8./1024./1024.);
  358.         ffprintf(o,s2,string);
  359.     }
  360.     ffprintf(o,s3,"MB/s");
  361.     
  362.     if(card->gdType!=fixedType){
  363.         ffprintf(o,s1,"GDSetEntries duration");
  364.         for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  365.             sprintf(string,"%.2f",card->depth[m].framesPerClutUpdate);
  366.             ffprintf(o,s2,string);
  367.         }
  368.         ffprintf(o,s3,"frames");
  369.  
  370.         ffprintf(o,s1,"GDSetEntries suppresses ints. for");
  371.         for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  372.             sprintf(string,"%.1f ",card->depth[m].missingFramesPerClutUpdate);
  373.             ffprintf(o,s2,string);
  374.         }
  375.         ffprintf(o,s3,"frames");
  376.  
  377.         ffprintf(o,s1,"GDSetEntriesHighPriority duration");
  378.         for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  379.             sprintf(string,"%.2f",card->depth[m].framesPerClutUpdateHighPriority);
  380.             ffprintf(o,s2,string);
  381.         }
  382.         ffprintf(o,s3,"frames");
  383.  
  384.         #if 0
  385.         // This is just for debugging, since it must equal the duration.
  386.         ffprintf(o,s1,"GDSetEntriesHighPriority suppr...");
  387.         for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  388.             sprintf(string,"%.1f ",card->depth[m].missingFramesPerClutUpdateHighPriority);
  389.             ffprintf(o,s2,string);
  390.         }
  391.         ffprintf(o,s3,"frames");
  392.         #endif
  393.  
  394.         if(card->setEntriesQuickly){
  395.             ffprintf(o,s1,"SetEntriesQuickly duration");
  396.             for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  397.                 sprintf(string,"%.2f",card->depth[m].framesPerClutUpdateQuickly);
  398.                 ffprintf(o,s2,string);
  399.             }
  400.             ffprintf(o,s3,"frames");
  401.         }
  402.         
  403.         hashTest=0;
  404.         for(m=0;m<6;m++)if(card->depth[m].pixelSize)
  405.             if(card->depth[m].clut.hashTest)hashTest=1;
  406.         if(hashTest){
  407.             ffprintf(o,s1,"GDSetEntries hash inspection");
  408.             for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  409.                 if(card->depth[m].clut.hashTest){
  410.                     if(card->depth[m].clut.hash)sprintf(string,"fail");
  411.                     else sprintf(string,"pass");
  412.                 }else sprintf(string,"");
  413.                 ffprintf(o,s2,string);
  414.             }
  415.             ffprintf(o,s3,"");
  416.         }
  417.  
  418.         if(card->setEntriesQuickly){
  419.             hashTest=0;
  420.             for(m=0;m<6;m++)if(card->depth[m].pixelSize)
  421.                 if(card->depth[m].clutQuickly.hashTest)hashTest=1;
  422.             if(hashTest){
  423.                 ffprintf(o,s1,"SetEntriesQuickly hash inspection");
  424.                 for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  425.                     if(card->depth[m].clutQuickly.hashTest){
  426.                         if(card->depth[m].clutQuickly.hash)sprintf(string,"fail");
  427.                         else sprintf(string,"pass");
  428.                     }else sprintf(string,"");
  429.                     ffprintf(o,s2,string);
  430.                 }
  431.                 ffprintf(o,s3,"");
  432.             }
  433.         }
  434.     
  435.         ffprintf(o,s1,"GDSetEntries write-read test");
  436.         for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  437.             clut=&card->depth[m].clut;
  438.             if(!card->isGray && clut->color.errors){
  439.                 if(!clut->gray.errors)sprintf(string,"!color");
  440.                 else if(!clut->color.zeroStartErrors)sprintf(string,"!serial");
  441.                 else sprintf(string,"bad");
  442.             } else if(card->isGray && clut->gray.errors){
  443.                 if(!clut->color.errors)sprintf(string,"!gray");
  444.                 else if(!clut->gray.zeroStartErrors)sprintf(string,"!serial");
  445.                 else sprintf(string,"bad");
  446.             }else sprintf(string,"ok ");
  447.             ffprintf(o,s2,string);
  448.         }
  449.         ffprintf(o,s3,"");
  450.  
  451.         visualTest=0;
  452.         for(m=0;m<6;m++)if(card->depth[m].pixelSize)
  453.             if(card->depth[m].clut.visual.tests)visualTest=1;
  454.         if(visualTest){
  455.             ffprintf(o,s1,"GDSetEntries visual comparison");
  456.             for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  457.                 clut=&card->depth[m].clut;
  458.                 if(clut->visual.tests){
  459.                     if(clut->visual.errors){
  460.                         if(!clut->visual.zeroStartErrors)sprintf(string,"!serial");
  461.                         else sprintf(string,"fail");
  462.                     }else sprintf(string,"pass");
  463.                 }else sprintf(string,"");
  464.                 ffprintf(o,s2,string);
  465.             }
  466.             ffprintf(o,s3,"");
  467.         }
  468.  
  469.         if(card->setEntriesQuickly){
  470.             ffprintf(o,s1,"SetEntriesQuickly write-read test");
  471.             for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  472.                 clut=&card->depth[m].clutQuickly;
  473.                 if(!card->isGray && clut->color.errors){
  474.                     if(!clut->gray.errors)sprintf(string,"!color");
  475.                     else if(!clut->color.zeroStartErrors)sprintf(string,"!serial");
  476.                     else sprintf(string,"bad");
  477.                 } else if(card->isGray && clut->gray.errors){
  478.                     if(!clut->color.errors)sprintf(string,"!gray");
  479.                     else if(!clut->gray.zeroStartErrors)sprintf(string,"!serial");
  480.                     else sprintf(string,"bad");
  481.                 }else sprintf(string,"ok ");
  482.                 ffprintf(o,s2,string);
  483.             }
  484.             ffprintf(o,s3,"");
  485.         }
  486.  
  487.         visualTest=0;
  488.         for(m=0;m<6;m++)if(card->depth[m].pixelSize)
  489.             if(card->depth[m].clutQuickly.visual.tests)visualTest=1;
  490.         if(visualTest){
  491.             ffprintf(o,s1,"GDSetEntriesQuickly visual comparison");
  492.             for(m=0;m<6;m++)if(card->depth[m].pixelSize){
  493.                 clut=&card->depth[m].clutQuickly;
  494.                 if(clut->visual.tests){
  495.                     if(clut->visual.errors){
  496.                         if(!clut->visual.zeroStartErrors)sprintf(string,"!serial");
  497.                         else sprintf(string,"fail");
  498.                     }else sprintf(string,"pass");
  499.                 }else sprintf(string,"");
  500.                 ffprintf(o,s2,string);
  501.             }
  502.             ffprintf(o,s3,"");
  503.         }
  504.     }
  505.     for(i=0;i<2;i++)if(o[i]!=NULL)fflush(o[i]);    // Save to disk, just in case.
  506. }
  507.  
  508. void GDRestoreBlackAndWhite(GDHandle device)
  509. // Restore the first & last clut entries (white & black) for
  510. // a clut whose mode need not correspond to what QuickDraw thinks, as
  511. // recorded in the device's GDHandle.
  512. {
  513.     short mode,colors,error;
  514.     ColorSpec white={255,0xffff,0xffff,0xffff},black={0,0,0,0};
  515.  
  516.     colors=GDClutSize(device);
  517.     GDGetMode(device,&mode,NULL,NULL);
  518.     if(mode<sixteenBitMode){
  519.         error=GDSetEntries(device,0,0,&white);
  520.         error=GDSetEntries(device,colors-1,0,&black-(colors-1));
  521.     }else{
  522.         error=GDDirectSetEntries(device,0,0,&black);
  523.         error=GDDirectSetEntries(device,colors-1,0,&white-(colors-1));
  524.     }
  525. }
  526.  
  527.  
  528.